package com.sikulifang.bank.simulation.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.hutool.db.sql.SqlBuilder;
import com.google.common.collect.Lists;
import com.sikulifang.bank.simulation.dto.FlowingWaterDTO;
import com.sikulifang.bank.simulation.dto.QueryCriteriaDTO;
import com.sikulifang.bank.simulation.dto.TransferPaymentDTO;
import com.sikulifang.bank.simulation.service.IBankMessage;
import com.sikulifang.bank.simulation.service.IDetailService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * ClassName DetailService
 * Description 明细表
 *
 * @author Yat
 * Date 2023/6/7 17:27
 * version 1.0
 */
@Slf4j
@Service
public class DetailServiceImpl extends ServiceImpl implements IDetailService {

    /**
     * 获取金额
     */
    private static BigDecimal getRandomMoney(BigDecimal min, BigDecimal max) {
        float minF = min.floatValue();
        float maxF = max.floatValue();

        // 生成随机数
        BigDecimal db = new BigDecimal(Math.random() * (maxF - minF) + minF);

        // 返回保留两位小数的随机数。不进行四舍五入
        return db.setScale(2, RoundingMode.DOWN);
    }

    /**
     * Description: 记录账号明细
     * <p>
     * Date: 2023/6/1 9:27
     */
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void updateDetail(Dict dict, String bankCode, boolean saveOrUpdate) {

        // 交易状态 0=等待;1=处理成功(默认)；2-交易失败
        int status = dict.getInt("status");
        // 交易描述
        String statusText = dict.getStr("statusText");

        DateTime dateTime = new DateTime();
        String date = DateUtil.format(dateTime, DatePattern.PURE_DATE_PATTERN);
        String time = DateUtil.format(dateTime, DatePattern.PURE_TIME_PATTERN);

        // 交易金额
        BigDecimal money = dict.getBigDecimal("money");

        Entity save = Entity.create(getTableName());
        // 自己的账号
        save.set("account_no", dict.get("card_id"));
        // 自己账号名称
        save.set("account_name", dict.get("account_name"));
        // 自己开户行名称
        save.set("bank_name", dict.get("open_bank_name"));
        // 余额
        save.set("balance", dict.get("balance"));
        // 对方账号
        save.set("opp_account_no", dict.get("oppAccountNo"));
        save.set("bank_no", dict.get("bankNo"));

        // 对方账户名称
        save.set("opp_account_name", dict.get("oppAccountName"));
        // 对方开户行名
        save.set("opp_bank_name", dict.get("oppOpenBankName"));
        // 对方开户 行 号
        save.set("opp_bank_no", dict.get("oppBankNo"));

        String serialNo = dict.getStr("serialNo");
        String batchSerialNo = dict.getStr("batchSerialNo");
        if (StrUtil.isBlank(batchSerialNo)) {
            batchSerialNo = serialNo;
        }

        save.set("type", 3);
        save.set("money", money);
        save.set("flag", dict.get("flag"));
        save.set("post_script", dict.get("postScript"));
        save.set("summary", dict.get("summary"));
        save.set("use_cn", dict.get("useCn"));
        save.set("serial_no", serialNo);
        save.set("batch_serial_no", batchSerialNo);
        save.set("status", status);
        save.set("status_text", statusText);
        save.set("tran_date", date);
        save.set("tran_time", time);
        save.set("create_time", DateUtil.formatDateTime(dateTime));
        save.set("additional", dict.get("additional", ""));
        save.set("bank_code", bankCode);

        if (saveOrUpdate) {
            db.insert(save);
        } else {
            Entity where = Entity.create(getTableName()).set("account_no", dict.get("card_id"))
                    .set("serial_no", serialNo)
                    .set("batch_serial_no", batchSerialNo);
            db.update(save, where);
        }
    }

    @SneakyThrows
    public Object[] updateDetail(Dict dict, String bankCode) {

        DateTime dateTime = new DateTime();
        String date = DateUtil.format(dateTime, DatePattern.PURE_DATE_PATTERN);
        String time = DateUtil.format(dateTime, DatePattern.PURE_TIME_PATTERN);

        String serialNo = dict.getStr("serialNo");
        String batchSerialNo = dict.getStr("batchSerialNo");
        if (StrUtil.isBlank(batchSerialNo)) {
            batchSerialNo = serialNo;
        }

        Object cardId = dict.get("card_id");
        Object accountName = dict.get("account_name");
        Object openBankName = dict.get("open_bank_name");
        Object balance = dict.get("balance");
        Object oppAccountNo = dict.get("oppAccountNo");
        Object bankNo = dict.get("bankNo");
        Object oppAccountName = dict.get("oppAccountName");
        Object oppBankNo = dict.get("oppBankNo");
        BigDecimal money = dict.getBigDecimal("money");
        Object flag = dict.get("flag");
        Object postScript = dict.get("postScript");
        Object summary = dict.get("summary");
        Object useCn = dict.get("useCn");
        Integer status = dict.getInt("status");
        String statusText = dict.getStr("statusText");
        String additional = dict.get("additional", "");
        Object oppOpenBankName = dict.get("oppOpenBankName");

        return new Object[]{
                cardId, accountName, openBankName, balance, oppAccountNo, bankNo,
                oppAccountName, oppOpenBankName, oppBankNo, 3, money, flag, postScript, summary,
                useCn, serialNo, batchSerialNo, status, statusText, date, time, DateUtil.formatDateTime(dateTime),
                additional, bankCode
        };
    }

    /**
     * Description: 创建回单
     * <p>
     * Date: 2023年6月9日, 0009 16:14
     *
     * @param entity 流水明细
     * @return {@link String}
     */
    @Override
    @SneakyThrows
    public String createPdf(Entity entity) {
        String filePath = entity.getStr("file_path");
        if (StrUtil.isNotBlank(filePath) && FileUtil.exist(filePath)) {
            return filePath;
        }
        File file = new File("pdf/" + entity.getStr("bank_code") + "/" + IdUtil.objectId() + ".pdf");
        File copy = FileUtil.copy(new File("pdf/template.pdf"), file, false);
        String pdf = copy.getAbsolutePath();

        // Map<String, MapValueDTO> map = PdfUtil.getMap(entity);
        // String pdf = PdfUtil.createPdf(map);
        entity.set("file_path", pdf);
        Object id = entity.remove("id");
        db.update(entity, Entity.create(getTableName()).set("id", id));

        return pdf;
    }

    @Override
    @SneakyThrows
    public Entity getDetailById(String id) {
        return db.get(Entity.create(getTableName()).set("id", id));
    }

    @Override
    @SneakyThrows
    public List<Entity> getDetailById(Set<String> id) {
        return db.find(Entity.create(getTableName()).set("id", id));
    }

    @Override
    @SneakyThrows
    public void createPdf(List<String> ids) {
        List<Entity> list = db.find(
                Entity.create(getTableName()).set("id", ids).set("status", 1)
        );

        for (Entity entity : list) {
            createPdf(entity);
        }

    }

    @Override
    @SneakyThrows
    public long findCountBySerialNo(String serialNo) {

        SqlBuilder builder = SqlBuilder.create().select("1").from(getTableName())
                .where("batch_serial_no = " + serialNo);

        Number number = db.count(builder.build());
        return number.longValue();

    }

    @Override
    public List<String> getQueryCriteria() {
        return ListUtil.toList("account_no", "account_name", "bank_name", "money");
    }

    @Override
    public Entity list(QueryCriteriaDTO queryCriteria) {
        Entity entity = super.list(queryCriteria);
        if (entity.get(dbProperties.getRecords()) instanceof List<?> dictList) {
            for (Object obj : dictList) {
                if (obj instanceof Dict data) {
                    data.set("money", new DecimalFormat("0.000000").format(data.get("money")));
                    data.set("balance", new DecimalFormat("0.000000").format(data.get("balance")));

                    String format = DateUtil.format(
                            DateUtil.parse(data.getStr("tranDate") + data.getStr("tranTime")),
                            DatePattern.CHINESE_DATE_TIME_PATTERN
                    );

                    data.set("tranDate", format);


                }
            }
        }
        return entity;
    }

    @Override
    @SneakyThrows
    public void batchAdd(FlowingWaterDTO flowingWater) {
        List<IBankMessage> bankMessages = getAccountServices();

        IBankMessage bankMessage = null;
        for (IBankMessage accountService : bankMessages) {
            if (flowingWater.getBankName().contains(accountService.bankCode())) {
                bankMessage = accountService;
                break;
            }
        }
        if (bankMessage == null) {
            throw new RuntimeException("未知银行");
        }

        TransferPaymentDTO payment = new TransferPaymentDTO();
        payment.setBatchSerialNo(IdUtil.getSnowflakeNextIdStr());

        payment.setPostScript("系统生成流水");
        payment.setSummary("系统生成流水");
        payment.setUseCn("系统生成流水");

        ArrayList<TransferPaymentDTO> TransferPaymentDTOList = Lists.newArrayList();

        for (List<String> info : flowingWater.getCollection()) {

            String oppBankName = info.get(0);
            String oppAccountName = info.get(1);
            String oppAccountNo = info.get(2);
            for (int i = 0; i < flowingWater.getTransferAccounts(); i++) {

                TransferPaymentDTO clone = payment.clone();
                clone.setPayAccountNo(flowingWater.getAccountNo());

                clone.setRecAccountNo(oppAccountNo);
                clone.setRecAccountName(oppAccountName);
                clone.setRecOpenBankName(oppBankName);

                clone.setSerialNo(IdUtil.getSnowflakeNextIdStr());
                BigDecimal money = getRandomMoney(
                        flowingWater.getMinMoney(), flowingWater.getMaxMoney()
                );
                clone.setMoney(money);
                TransferPaymentDTOList.add(clone);
            }
        }
        bankMessage.tradingEntrance(TransferPaymentDTOList);
    }

    @Override
    @SneakyThrows
    public List<Entity> findByBankCode(String bankCode, String tranDate) {

        Entity entity = Entity.create(getTableName())
                .set("bank_code", bankCode)
                .set("tran_date", tranDate);

        return db.findAll(entity);
    }

    @Override
    @SneakyThrows
    public List<Entity> findBySerialNo(Collection<String> serialNoS, String batchSerialNo) {

        Entity entity = Entity.create(getTableName())
                .set("serial_no", serialNoS)
                .set("flag", "D")
                .set("batch_serial_no", batchSerialNo);

        return db.findAll(entity);
    }
}
